enum LightType
{
	DIRECTED_LIGHT = 0,
	POINT_LIGHT,
	SPOT_LIGHT
};

struct Light
{
	float3 pos;
    float3 dir;
    float4 ambient;
    float4 diffuse;
    float4 spec;
    float3 att; // attenuation parameters (a0, a1, a2)
    float3 spotAtt;
    float  range;
	LightType type;
};

struct SurfaceInfo
{
	float3 pos;
	float3 normal;
	float4 diffuse;
	float4 spec;
};

float attenuation(float distance, float3 coeff)
{
	float att = 1.0f / dot(coeff, float3(distance * distance, distance, 1,0f));

	//return step(distance, range) * saturate(att);
	return att;
}

float3 directionalLight(SurfaceInfo s, Light l, float3 eyePos)
{
	float3 litColor = float3(0.0f, 0.0f, 0.0f);
	float3 lightVec = -l.dir;
	litColor += s.Diffuse * l.ambient;

	float diffuseFactor = dot(lightVec, s.normal);
	if(diffuseFactor > 0.0f)
	{
		float specPower = max(s.spec.a, 1.0f);
		float3 toEye = normalize(eyePos - s.pos);
		float3 r = reflect(-lightVec, s.normal);
		float specFactor = pow(max(dot(r, toEye),0.0f),specPower);

		litColor += diffuseFactor * s.diffuse * l.diffuse;
		litColor += specFactor * s.spec * l.spec;
	}

	return litColor;
}

float3 pointLight(SurfaceInfo s, Light l, float3 eyePos)
{
	float3 litColor = float3(0.0f, 0.0f, 0.0f);

    float3 lightVec = l.pos - s.pos;

    // The distance from surface to light.
    float d = length(lightVec);

    if( d > l.range )
        return float3(0.0f, 0.0f, 0.0f);

    // Normalize the light vector.
    lightVec /= d;

    // Add the ambient light term.
    litColor += s.diffuse * l.ambient;

    float diffuseFactor = dot(lightVec, s.normal);
    [branch]
    if( diffuseFactor > 0.0f )
	{
        float specPower  = max(s.spec.a, 1.0f);
        float3 toEye     = normalize(eyePos - s.pos);
        float3 R         = reflect(-lightVec, s.normal);
        float specFactor = pow(max(dot(R, toEye), 0.0f), specPower);

        // diffuse and specular terms
        litColor += diffuseFactor * s.diffuse * l.diffuse;
        litColor += specFactor * s.spec * l.spec;
    }

    // attenuate
    return litColor * attenuation(d, l.att);
}

float3 spotLight(SurfaceInfo s, Light l, float3 eyePos)
{
	float3 litColor = pointLight(s, l, eyePos);
	float3 lightVec = normalize(l.pos - s.pos);
	float cosAlpha = max(0.0f, dot(-lightVec, l.dir));

	float spotAtten = 0.0f;
	float cosTheta = l.spotAtt.x;
	float cosPhi = l.spotAtt.y;
	if(cosAlpha > cosTheta)
	{
		spotAtten = 1.0f;
	}
	else if(cosAlpha > cosPhi)
	{
		spotAtten = pow((cosAlpha - cosPhi) / (cosTheta - cosPhi), l.spotAtt.z);
	}

	return litColor * spotAtten;
}